package nachos.threads;

import java.util.LinkedList;

import nachos.machine.*;

/**
 * An implementation of condition variables that disables interrupt()s for
 * synchronization.
 *
 * <p>
 * You must implement this.
 *
 * @see	nachos.threads.Condition
 */
public class Condition2 {
    /**
     * Allocate a new condition variable.
     *
     * @param	conditionLock	the lock associated with this condition
     *				variable. The current thread must hold this
     *				lock whenever it uses <tt>sleep()</tt>,
     *				<tt>wake()</tt>, or <tt>wakeAll()</tt>.
     */
    public Condition2(Lock conditionLock) {
    	this.conditionLock = conditionLock;

    	//2012.3.2 added by Sylvia
    	waitList = new LinkedList<KThread>();
    	
    }

    /**
     * Atomically release the associated lock and go to sleep on this condition
     * variable until another thread wakes it using <tt>wake()</tt>. The
     * current thread must hold the associated lock. The thread will
     * automatically reacquire the lock before <tt>sleep()</tt> returns.
     */
    public void sleep() {
		Lib.assertTrue(conditionLock.isHeldByCurrentThread());
	
		
		
		//2012.3.2 added by Sylvia
		waitList.add(KThread.currentThread());
		conditionLock.release();
		boolean intStat = Machine.interrupt().disable();
    	KThread.sleep();
    	Machine.interrupt().restore(intStat);
    	conditionLock.acquire();
    	///////////////////////////
    	
		
    }

    /**
     * Wake up at most one thread sleeping on this condition variable. The
     * current thread must hold the associated lock.
     */
    public void wake() {
    	Lib.assertTrue(conditionLock.isHeldByCurrentThread());
	
    	//2012.3.2 added by Sylvia
    	boolean intStatus = Machine.interrupt().disable();
    	if (!waitList.isEmpty()){
    		waitList.removeFirst().ready();
    	}    	
    	Machine.interrupt().restore(intStatus);
    	///////////////////////////////
    }

    /**
     * Wake up all threads sleeping on this condition variable. The current
     * thread must hold the associated lock.
     */
    public void wakeAll() {
    	Lib.assertTrue(conditionLock.isHeldByCurrentThread());
    	//2012.3.2 added by Sylvia
    	while (!waitList.isEmpty()){
    		wake();
    	}
    	///////////////////////////////	
    }

    private Lock conditionLock;
    
    //2012.3.2 added by sylvia 
    private LinkedList<KThread> waitList;   
    
    public static void selfTest(){
    	Lock lock = new Lock();
    	Condition c1 = new Condition(lock);
    	Condition c2 = new Condition(lock);
    	for (int i = 0; i < 6; i ++){
    		new KThread(new CondThread(i, lock, c1, c2)).fork();
    	}
    	System.out.println("main sleeping...");
    	lock.acquire();
    	c2.sleep();
    	lock.release();
    	System.out.println("main wakened...");
    }
        
    private static class CondThread implements Runnable{
    	private CondThread(int _iter, Lock _lock, Condition _cond1, Condition _cond2){
    		iter = _iter;
    		lock = _lock;
    		cond1 = _cond1;
    		cond2 = _cond2;
    		System.out.println("creating "+ iter);
    	}
		public void run(){			
			if (iter < 5){
				System.out.println(iter + " sleeping...");
				lock.acquire();
				cond1.sleep();
				lock.release();
				System.out.println(iter + " wakened...");
				if (iter == 4){
					System.out.println("4 waking main..");
					lock.acquire();
					cond2.wake();
					lock.release();
				}
			}
			else {
				System.out.println(iter + " waking all...");
				lock.acquire();
				cond1.wakeAll();
				lock.release();
			}			
			
		}
		int iter;
		Lock lock;
		Condition cond1, cond2;
    }
}
